
#ifndef __C_OPENGL_MATERIAL_RENDERER_H_INCLUDED__
#define __C_OPENGL_MATERIAL_RENDERER_H_INCLUDED__

#include "IrrCompileConfig.h"


#include <GL/gl.h>
#include <GL/glu.h>


#include "CPSPGLDriver.h"
#include "IMaterialRenderer.h"
/* Color Macros, maps 8 bit unsigned channels into one 32-bit value */
#define GU_ABGR(a,b,g,r)        (((a) << 24)|((b) << 16)|((g) << 8)|(r))
#define GU_ARGB(a,r,g,b)        GU_ABGR((a),(b),(g),(r))
#define GU_RGBA(r,g,b,a)        GU_ARGB((a),(r),(g),(b))

/* Color Macro, maps floating point channels (0..1) into one 32-bit value */
#define GU_COLOR(r,g,b,a)       GU_RGBA((u32)((r) * 255.0f),(u32)((g) * 255.0f),(u32)((b) * 255.0f),(u32)((a) * 255.0f))


extern "C"
{
    void glBlendFuncFixPSP (GLuint srccolor, GLuint dstcolor);
}
namespace irr
{
    namespace video
    {

        //! Base class for all internal D3D9 material renderers
        class COpenGLMaterialRenderer : public IMaterialRenderer
        {
        public:

            //! Constructor
            COpenGLMaterialRenderer(video::CPSPGLDriver* driver)
                : Driver(driver)
            {
            }

        protected:

            video::CPSPGLDriver* Driver;
        };


        class COpenGLMultipassRender 
		{

        public:
            virtual bool setPass(int i);
        };


        class COpenGLMultipassRender_dummy : public COpenGLMultipassRender
        {
            virtual bool setPass(int i)
            {
                switch (i)
                {
                case 0:
                    return true;
                    break;
                case 1:
                    return false;
                    break;
                }
            }
        };


        class COpenGLMultipassRender_REFLECTION_2_LAYER : public COpenGLMultipassRender
        {
            virtual bool setPass(int i)
            {
                switch (i)
                {
                case 0:
                    glDisable(GL_ALPHA_TEST);
                    glDisable(GL_BLEND);


                    return true;
                    break;

                case 1:
                    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                  /*   glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); */

                   glBlendFuncFixPSP(
                        GU_COLOR(0.3f, 0.3f, 0.3f, 0.f),
                        GU_COLOR(0.7f, 0.7f, 0.7f, 0.f));
                 
                    glEnable(GL_BLEND);
                    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
                    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

                    glEnable(GL_TEXTURE_GEN_S);
                    glEnable(GL_TEXTURE_GEN_T);
                    return true;
                    break;
                }
            }
        };


        class COpenGLMultipassRender_TRANSPARENT_REFLECTION_2_LAYER : public COpenGLMultipassRender
        {
            virtual bool setPass(int i)
            {
                switch (i)
                {
                case 0:
                    glDisable(GL_ALPHA_TEST);
                    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
                    glEnable(GL_BLEND);

                    return true;
                    break;

                case 1:
                    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
                    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

                    glEnable(GL_TEXTURE_GEN_S);
                    glEnable(GL_TEXTURE_GEN_T);
                    return true;
                    break;
                }
            }
        };

        class COpenGLMultipassRender_SOLID_2_LAYER : public COpenGLMultipassRender
        {
            virtual bool setPass(int i)
            {
                switch (i)
                {
                case 0:
                    glDisable(GL_ALPHA_TEST);
                    glDisable(GL_BLEND);


                    return true;
                    break;

                case 1:
                    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
                    glEnable(GL_BLEND);
                    return true;
                    break;
                }
            }
        };

        //! Solid material renderer
        class COpenGLMaterialRenderer_SOLID : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_SOLID(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {
            }

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {

                    // thanks to Murphy, the following line removed some bugs with several OpenGL
                    // implementations.

#if 1
                    glDisable(GL_BLEND);
                    glDisable(GL_ALPHA_TEST);
#endif
                }
                Driver->setMultipassRender(0);
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }
        };


        //! Solid 2 layer material renderer
        class COpenGLMaterialRenderer_SOLID_2_LAYER : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_SOLID_2_LAYER(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {
            }

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {
                }
                Driver->setMultipassRender(3);
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }
        };


        //! Transparent add color material renderer
        class COpenGLMaterialRenderer_TRANSPARENT_ADD_COLOR : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_TRANSPARENT_ADD_COLOR(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {}

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                //if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {
                    glDisable(GL_ALPHA_TEST);

                    Driver->setTexture(1,0);

                    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
                    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                    glEnable(GL_BLEND);
                }

                Driver->setMultipassRender(0);
                material.ZWriteEnable = false;
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }

            //! Returns if the material is transparent. 
            virtual bool isTransparent() 
            {
                return true; 
            }
        };


        //! Transparent vertex alpha material renderer
        class COpenGLMaterialRenderer_TRANSPARENT_VERTEX_ALPHA : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_TRANSPARENT_VERTEX_ALPHA(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {}

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {
                    glDisable(GL_ALPHA_TEST);

                    Driver->setTexture(1,0);


                    glDisable(GL_ALPHA_TEST);

                    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
                    glEnable(GL_BLEND);
                }

                Driver->setMultipassRender(0);
                material.ZWriteEnable = false;
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }

            virtual void OnUnsetMaterial()
            {
                // default values
#if 1
                glDisable(GL_BLEND);
#endif
            }

            //! Returns if the material is transparent.
            virtual bool isTransparent()
            {
                return true;
            }
        };


        //! Transparent alpha channel material renderer
        class COpenGLMaterialRenderer_TRANSPARENT_ALPHA_CHANNEL : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_TRANSPARENT_ALPHA_CHANNEL(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {}

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {



                    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
                    glEnable(GL_BLEND);

                    glEnable(GL_ALPHA_TEST);

                    f32 refValue = material.MaterialTypeParam;
                    if ( refValue == 0 )
                        refValue = 0.5f;

                    glAlphaFunc(GL_GREATER, refValue);
                }

                Driver->setMultipassRender(0);
                //material.ZWriteEnable = false;
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }

            virtual void OnUnsetMaterial() 
            { 
                glDisable(GL_ALPHA_TEST); 
            }

            //! Returns if the material is transparent. 
            virtual bool isTransparent() 
            {
                return true; 
            }
        };



        //! Transparent alpha channel material renderer
        class COpenGLMaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {}

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {
                    glEnable(GL_ALPHA_TEST);
                    glDisable(GL_BLEND);

                    glAlphaFunc(GL_GREATER, 0.5);

                }
                Driver->setMultipassRender(0);
                //material.ZWriteEnable = false;
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }

            virtual void OnUnsetMaterial()
            {
                glDisable(GL_ALPHA_TEST);
            }

            //! Returns if the material is transparent. 
            virtual bool isTransparent() 
            {
                return false;  // this material is not really transparent because it does no blending.
            }
        };


        //! material renderer for all kinds of linghtmaps
        class COpenGLMaterialRenderer_LIGHTMAP : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_LIGHTMAP(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {}

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {

                }
                Driver->setMultipassRender(0);
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }
        };



        //! material renderer for all kinds of linghtmaps
        class COpenGLMaterialRenderer_DETAIL_MAP : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_DETAIL_MAP(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {}

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {

                }
                Driver->setMultipassRender(0);
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }
        };


        //! sphere map material renderer
        class COpenGLMaterialRenderer_SPHERE_MAP : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_SPHERE_MAP(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {}

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {


                    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

                    glDisable(GL_BLEND);
                    glDisable(GL_ALPHA_TEST);

                    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
                    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);

                    glEnable(GL_TEXTURE_GEN_S);
                    glEnable(GL_TEXTURE_GEN_T);
                }

                Driver->setMultipassRender(0);
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }

            virtual void OnUnsetMaterial()
            {

                glDisable(GL_TEXTURE_GEN_S);
                glDisable(GL_TEXTURE_GEN_T);
            }
        };


        //! reflection 2 layer material renderer
        class COpenGLMaterialRenderer_REFLECTION_2_LAYER : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_REFLECTION_2_LAYER(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {}

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {
                    Driver->setMultipassRender(1);
                }
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }

            virtual void OnUnsetMaterial()
            {


                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
                glDisable(GL_TEXTURE_GEN_S);
                glDisable(GL_TEXTURE_GEN_T);

            }
        };


        //! reflection 2 layer material renderer
        class COpenGLMaterialRenderer_TRANSPARENT_REFLECTION_2_LAYER : public COpenGLMaterialRenderer
        {
        public:

            COpenGLMaterialRenderer_TRANSPARENT_REFLECTION_2_LAYER(video::CPSPGLDriver* d)
                : COpenGLMaterialRenderer(d) {}

            virtual void OnSetMaterial(SMaterial& material, const SMaterial& lastMaterial,
                bool resetAllRenderstates, IMaterialRendererServices* services)
            {
                if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates)
                {

                }
                Driver->setMultipassRender(2);
                material.ZWriteEnable = false;
                services->setBasicRenderStates(material, lastMaterial, resetAllRenderstates);
            }

            virtual void OnUnsetMaterial()
            {
                glDisable(GL_TEXTURE_GEN_S);
                glDisable(GL_TEXTURE_GEN_T);
            }

            //! Returns if the material is transparent.
            virtual bool isTransparent()
            {
                return true;
            }
        };

    } // end namespace video
} // end namespace irr

#endif
